package com.knight.spring.framework.aop.support;

import com.knight.spring.framework.aop.adapter.KnightAfterThrowingAdviceInterceptor;
import com.knight.spring.framework.aop.adapter.KnightMethodBeforeAdviceInterceptor;
import com.knight.spring.framework.aop.adapter.KnightReturningAdviceInterceptor;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class KnightAdvisedSupport {


    private final KnightAopConfig aopConfig;

    private Class<?> targetClass;

    private Object target;
    private transient Map<Method, List<Object>> methodCache;
    private Pattern pointCutClassPattern;
    private String strPointCutClassPattern;

    public KnightAdvisedSupport(KnightAopConfig aopConfig){
        this.aopConfig = aopConfig;
    }


    public Class<?> getTargetClass() {
        return targetClass;
    }

    public void setTargetClass(Class<?> targetClass) {
        this.targetClass = targetClass;
        parse();
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;

    }

    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
        List<Object> chains = methodCache.get(method);
        if (chains==null || chains.size()==0) {
            Method m = null;
            try {
                m = targetClass.getMethod(method.getName(),method.getParameterTypes());
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            chains = methodCache.get(m);

            //底层逻辑，对代理方法进行一个兼容处理
            this.methodCache.put(m,chains);
        }
        return chains;
    }

    private void parse() {
        String pointCut = aopConfig.getPointCut()
                .replaceAll("\\.","\\\\.")
                .replaceAll("\\\\.\\*",".*")
                .replaceAll("\\(","\\\\(")
                .replaceAll("\\)","\\\\)");
        //pointCut=public .* com.knight.spring.demo.service..*Service..*(.*)
        //玩正则
        String pointCutForClassRegex = pointCut.substring(0,pointCut.lastIndexOf("\\(") - 4);
        strPointCutClassPattern = "class " + pointCutForClassRegex.substring(
                pointCutForClassRegex.lastIndexOf(" ") + 1)+".*";
        pointCutClassPattern = Pattern.compile(strPointCutClassPattern);

        try {
            //执行器链
            List<Object> advices = new LinkedList<>();
            methodCache = new HashMap<>();
            Pattern pattern = Pattern.compile(pointCut);



            Class aspectClass = Class.forName(this.aopConfig.getAspectClass());
            Map<String,Method> aspectMethods = new HashMap<>();
            for (Method m : aspectClass.getMethods()) {
                aspectMethods.put(m.getName(),m);
            }

            for (Method m : this.targetClass.getMethods()) {
                String methodString = m.toString();
                if (methodString.contains("throws")) {
                    methodString = methodString.substring(0, methodString.lastIndexOf("throws")).trim();
                }

                Matcher matcher = pattern.matcher(methodString);
                if(matcher.matches()){
                    //把每一个方法包装成 MethodIterceptor
                    //before
                    if(!(null == aopConfig.getAspectBefore() || "".equals(aopConfig.getAspectBefore()))) {
                        //创建一个Advivce
                        advices.add(new KnightMethodBeforeAdviceInterceptor(aspectMethods.get(aopConfig.getAspectBefore()),
                                aspectClass.newInstance()));
                    }
                    //after
                    if(!(null == aopConfig.getAspectAfter() || "".equals(aopConfig.getAspectAfter()))) {
                        //创建一个Advivce
                        advices.add(new KnightReturningAdviceInterceptor(aspectMethods.get(aopConfig.getAspectAfter()),aspectClass.newInstance()));
                    }
                    //afterThrowing
                    if(!(null == aopConfig.getAspectAfterThrow() || "".equals(aopConfig.getAspectAfterThrow()))) {
                        //创建一个Advivce
                        KnightAfterThrowingAdviceInterceptor throwingAdvice =
                                new KnightAfterThrowingAdviceInterceptor(
                                        aspectMethods.get(aopConfig.getAspectAfterThrow()),
                                        aspectClass.newInstance());
                        throwingAdvice.setThrowName(aopConfig.getAspectAfterThrowingName());
                        advices.add(throwingAdvice);
                    }
                    methodCache.put(m,advices);
                }

            }
        }catch (Exception e){
            e.printStackTrace();
        }


    }


    public boolean pointCutMatch() {
        System.out.println("targetClass to string:"+targetClass.toString());
        String pattern = "class com\\.knight\\.demo\\.service\\..*Service.*";
        boolean matches = Pattern.matches(strPointCutClassPattern, targetClass.toString());
        System.out.println("result:"+matches);
        return pointCutClassPattern.matcher(this.targetClass.toString()).matches();
    }
}
